
#include <assert.h>

#include "Universe.hpp"
#include "OutOrNode.hpp"
#include "ValueNode.hpp"

OutOrNode::OutOrNode(bool exclusive) :
        ExpressionNode(),
        _exclusive(exclusive),
        _child(0)
{

}

OutOrNode::~OutOrNode()
{

}

Tribool OutOrNode::solve(Universe &universe)
{
    if (!this->_solved)
    {
        assert(this->_child != 0);
        this->_value = this->_child->solve(universe);
        this->_solved = true;
    }
    if (this->_value.value == TriboolVal::OutOrStopped)
        return Tribool(TriboolVal::OutOrStopped);
    else if (this->_value.value == TriboolVal::Undefined)
        return Tribool(TriboolVal::Undefined);
    else if (this->_value.value == TriboolVal::False)
    {
        if (this->_child->getType() == NodeType::OutOr)
        {
            if (this->_exclusive)
            {
                this->_duplicateUniverse(universe, TriboolVal::True, TriboolVal::True);
                this->_duplicateUniverse(universe, TriboolVal::False, TriboolVal::False);
            }
            else
                return Tribool(TriboolVal::False);
        }
        else
            return Tribool(TriboolVal::Undefined);
    }
    else
    {
        this->_duplicateUniverse(universe, TriboolVal::True, TriboolVal::False);
        this->_duplicateUniverse(universe, TriboolVal::False, TriboolVal::True);
        if (!this->_exclusive)
            this->_duplicateUniverse(universe, TriboolVal::True, TriboolVal::True);
    }
    return Tribool(TriboolVal::OutOrStopped);
}

void OutOrNode::_duplicateUniverse(Universe& universe, Tribool left, Tribool right)
{
    universe.duplicate();
    OutOrNode* outOrNodeCopy = dynamic_cast<OutOrNode*>(universe.getLastChild().getNodeById(this->_id));
    assert(outOrNodeCopy != 0 && outOrNodeCopy->_leftParent != 0 && outOrNodeCopy->_rightParent != 0);
    outOrNodeCopy->_leftParent->setValue(left);
    outOrNodeCopy->_rightParent->setValue(right);
    if (outOrNodeCopy->_leftParent->getType() == NodeType::Value)
    {
        char name = dynamic_cast<ValueNode*>(outOrNodeCopy->_leftParent)->getName();
        universe.getLastChild().addSpecificFacts(name);
        universe.getLastChild().addFact(name, left);
    }
    if (outOrNodeCopy->_rightParent->getType() == NodeType::Value)
    {
        char name = dynamic_cast<ValueNode*>(outOrNodeCopy->_rightParent)->getName();
        universe.getLastChild().addSpecificFacts(name);
        universe.getLastChild().addFact(name, right);
    }
}

ExpressionNode* OutOrNode::clone(std::map<int, ExpressionNode*>& existingNodes)
{
    if (existingNodes.find(this->_id) != existingNodes.end())
        return existingNodes[this->_id];
    OutOrNode* clone = new OutOrNode();
    clone->_id = this->_id;
    clone->_solved = this->_solved;
    clone->_value = this->_value;
    clone->_exclusive = this->_exclusive;
    clone->_leftParent = (this->_leftParent != 0) ? this->_leftParent->clone(existingNodes) : 0;
    clone->_rightParent = (this->_rightParent != 0) ? this->_rightParent->clone(existingNodes) : 0;
    existingNodes[this->_id] = clone;
    clone->_child = (this->_child != 0) ? this->_child->clone(existingNodes) : 0;
    return clone;
}

void OutOrNode::addChild(ExpressionNode *child)
{
    this->_child = child;
}
void OutOrNode::setLeftParent(ExpressionNode* parent)
{
    this->_leftParent = parent;
}

void OutOrNode::setRightParent(ExpressionNode* parent)
{
    this->_rightParent = parent;
}

NodeType::Type OutOrNode::getType() const
{
    return NodeType::OutOr;
}
